Skip to content

The run file is the module __main__ (P37) - #49

Merged
cedricvogel-leo merged 1 commit into
mainfrom
feature/main-module
Nov 4, 2026
Merged

cedricvogel-leo merged 1 commit into
mainfrom
feature/main-module

Conversation

@cedricvogel-leo

Copy link
Copy Markdown
Collaborator

What

As in python3 main.py, the run file is now the module sys.modules["__main__"]:

x = 2
import __main__
print(__main__.x)    # 2 (before: AttributeError)
print(__main__)      # <module '__main__' from '/main.py'> (before: … BuiltinImporter …)

What each part of glpython does:

  • ▶: each Run creates a fresh module __main__, runs the file in its namespace and installs it as sys.modules["__main__"]. __name__ == "__main__" stays true, so if __name__ == "__main__": runs.
  • REPL after ▶: it works in that same namespace, in both directions: y = 5 → __main__.y is 5, and __main__.z = 7 → z is 7. Before any Run, the REPL's own namespace is __main__, as in Python's REPL.
  • «Tests»:
    • Its Run part is a ▶ like any other.
    • Its doctests still load the file as a module named after it, as python -m doctest does, so the if __name__ == "__main__": block doesn't run there.
    • In a doctest example, __main__ is the module of the Tests' Run.
  • Visualizer:
    • The traced file is __main__ while it runs, and the session's __main__ is back afterwards.
    • A traced REPL input (or doctest example) sees the file's variables through __main__ and remains a dry run: __main__.y = 99 changes nothing in the session.
  • Visualizing a REPL entry (replay): the replay runs in a __main__ of its own and restores the session's at the end.
  • sys._getframe(1).f_globals from a function in another file still reads the caller's variables after ▶ and in the REPL, as the course uses it.

One fix in the visualizer was needed: the fallback that runs an import dropped every name starting with __, so import __main__ never bound the name. It now skips only __builtins__ and dunder names the statement didn't change.

Checks

  • New scripts/check-runtime-main.py, added to test:runtime:
    • Run: the output equals CPython's for the same files (__main__.x, print(__main__), __main__ is sys.modules["__main__"], the if __name__ block, _getframe(1).f_globals and __main__ from hilfe.py).
    • REPL: __main__.x, __main__.y after y = 5, z after __main__.z = 7, hilfe.lies("y") with _getframe. The namespace is the module's.
    • REPL before a Run: import __main__; __main__.a works.
    • Visualizer: the same program gives the same output, and the REPL's __main__ is back afterwards.
    • Traced REPL input: it reads __main__.y as a dry run.
    • Replay: it uses a module of its own, then the session's is restored.
  • scripts/check-runtime-replay.py: its fresh-worker case now starts from a __main__ module, as the worker does.
  • scripts/check-console-browser.mjs, in Pyodide: a Run prints __main__.x and runs the if __name__ block, and __main__.y * 10 in the REPL gives 50.
  • Locally, lint, test:runtime, test:protocol and test:console pass.

How it goes live

Manual tests (desktop, and iPad with an external keyboard)

  1. ▶ the following. It prints 2, then <module '__main__' from '/main.py'>, then als Programm:
    x = 2
    import __main__
    print(__main__.x)
    print(__main__)
    if __name__ == "__main__":
        print("als Programm")
  2. In the REPL after that, y = 5 then __main__.y gives 5, and __main__.z = 7 then z gives 7.
  3. Visualize the file from 1. The console output is the same, and __main__.x shows 2. Afterwards, __main__.y in the REPL still gives 5.
  4. Course check VA-2 (sys._getframe(1).f_globals from the library file) passes as before.
  5. «Tests» on a file with a doctest and an if __name__ == "__main__": block: the doctests pass, and the block doesn't run during the doctests.

Follow-ups (not fixed)

  • sys._getframe(1).f_globals in the visualizer (unchanged, the same on main): a function from another file is real Python there, and its caller's frame is the visualizer's interpreter, so f_globals holds the runtime's names (KeyError: 'x'). After ▶ and in the REPL it works. import __main__ from the library now works everywhere, the visualizer included, if the course ever wants a way that works there too.
  • __file__ in the visualizer is main.py and in a Run it is /main.py (pre-existing). So print(__main__) differs between the two.
  • «Tests» doesn't register the file under its module name in sys.modules (e.g. sys.modules["main"]), as python -m doctest does. Not needed so far.

🤖 Generated with Claude Code

Run, the REPL, a replay and the visualizer run the file in a fresh module
installed as sys.modules["__main__"], so "import __main__; __main__.x" sees
its variables as in CPython. A trace and a replay restore the session's
module afterwards; a traced REPL input stays a dry run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cedricvogel-leo
cedricvogel-leo merged commit 118cf5c into main Nov 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant